//
//  extUart.c
//  NMEA
//
//  Created by Hans Johnson on 2/6/2014.
//
//

#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "avr_compiler.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "dma.h"
#include "uart.h"

/* External UART divisors lookup table */
const __flash int extBaudDivisors[] = {
    0x0300, // 1200
    0x0180, // 2400
    0x00C0, // 4800
    0x0060, // 9600
    0x0030, // 19200
    0x0018, // 38400
    0x0010, // 57600
    0x0008  // 115200
};

/* Internal USART divisors lookup table */
const __flash int intBaudDivisors[] = {
    0x031D, // Baud: 1200
    0x01ED, // Baud: 2400
    0xFDDC, // Baud: 4800
    0xF5CC, // Baud: 9600
    0xE5BC, // Baud: 19200
    0xC5AC, // Baud: 38400
    0x6EA8 // Baud: 57600
};

/* UART Interrupt Status Saving */
uint8_t uartIntStatus[8];

/* Synchronization semaphores  used for Tx/Rx*/
xSemaphoreHandle uartTxSync[12];
xSemaphoreHandle uartRxSync[12];

// Synchronization variables for internal uarts
// Current Send character position
uint8_t intUartSendPos[4];
// Length of string to Send
uint8_t intUartSendLength[4];
// String Pointer for string to send
char *intUartSendString[4];
// Current Rx character Position
uint8_t intUartRxPos[4];
// Length of string to Receive
uint8_t intUartRxLength[4];
// String Pointer for string to rx
char *intUartRxString[4];
// Termination Character for Rx
char intUartTerm[4];


/* Function to set the UART to 8N1 */
void uartSet8N1 (void *Uart)
{
    // The external UARTs are at higher addresses than the internal uarts
    if ((uint16_t)Uart >= UART_BASE_ADDR)
    {
        UART_REG_t *extUart = (UART_REG_t*)Uart;
        /* Set LCR[1:0] to generate 8 Bits */
        extUart->UART_LCR |= 0x03;
        /* Clear LCR[2] to generate 1 stop bit */
        extUart->UART_LCR &= ~(0x01<<2);
        /* Clear LCR[3] to disable parity */
        extUart->UART_LCR &= ~(0x01<<3);
    }
    else // Internal USART
    {
        USART_t *intUart = (USART_t*)Uart;
        // clear CMODE, PMODE and SBMODE to 0, CHSIZE to 8 bit.
        intUart->CTRLC = USART_CHSIZE_8BIT_gc;
    }
};

/* Function to setup the BAUD Rate on the 16550 UART */
void uartSetBaud (void *Uart, uint8_t BaudDivisor)
{
    // utility variable
    uint8_t temp;
    
    // the external UARTs are at higher addresses than theinternal ones
    if ((uint16_t)Uart >= UART_BASE_ADDR)
    {
        // Cast the void pointer to a UART type
        UART_REG_t *extUart = (UART_REG_t*)Uart;

        /* Diable Interrupts for this section */
        cli();
        /* Save LCR */
        temp = extUart->UART_LCR;
        /* Set LCR[7] to 1 */
        extUart->UART_LCR = (0x01 << 7);
        /* Load the bytes into the Divisor Latch */
        extUart->UART_RHR_THR = (uint8_t)(extBaudDivisors[BaudDivisor] & 0x00FF);
        extUart->UART_IER = (uint8_t)(BaudDivisor >> 8);
        /* Restore LCR */
        extUart->UART_LCR = temp;
        /* Reinable the interrupts */
        sei();
    }
    else
    {
        // Cast the void pointer to a USART type
        USART_t *intUart = (USART_t*)Uart;
        
        // Copy the data into the baud control registers
        intUart->BAUDCTRLB = (uint8_t)(intBaudDivisors[BaudDivisor] & 0x00FF);
        intUart->BAUDCTRLA = (uint8_t)(intBaudDivisors[BaudDivisor] >> 8);
    };
};

/* Function to set the transmit trigger level on the external uarts */

void extUartTxTLR (UART_REG_t *extUart, uint8_t level)
{
    uint8_t temp;
    /* Set MCR[6] to 1 to allow the setting of the TLR */
    extUart->UART_MCR |= (1<<6);
    // read in the TLR register and zero out the lower nibble
    temp = extUart->UART_SPR & 0xF0;
    // Set the lower nibble to the level /4 and zero out the top nibble
    temp |= ((level >> 2) & 0x0F);
    // write it back to the register
    extUart->UART_SPR = temp;
    // clear MCR[6] to return to normal operations
    extUart->UART_MCR &= ~(1<<6);
}

/* Function to setup the Interrupts for the UART */
void uartInit (void *Uart)
{
    /* Temporary register holder */
    uint8_t temp;
    // utility register cast the void pointer
    UART_REG_t *extUart = (UART_REG_t*)Uart;
    /* Configure UARTS */
    if ((uint16_t)Uart >= UART_BASE_ADDR)
    {
        /* If it's one of the 16550s */
        
        /* Store the LCR register temporarily */
        temp = extUart->UART_LCR;
        
        /* Enable the Enhanced Features of the UART */
        extUart->UART_LCR = 0xbf;
        extUart->UART_IIR_FCR = (1<<4);
        
        /* Reset LCR so we can use it normally */
        extUart->UART_LCR = temp;
        /* Enable the FIFOs and clear them This is done by setting FCR[2:0] to 1*/
        extUart->UART_IIR_FCR = ((1<<2)|(1<<1)|(1<<0));
        
        /* Disable all the interrupts on the 16550s */
        extUart->UART_IER = 0x00;
        
    }
    else
    {
        
    };
    /* Configure the Atmel Interrupts and setup the semaphores*/
    switch ((uint16_t)Uart) {
        case (uint16_t)&EXTUART0:
            /* PORT A, PIN 1 */
            /* Clear the bit in the DIR register */
            PORTA.DIR &= ~PIN1_bm;
            /* Set the pin for inverted input and Level Sensing Interrupt*/
            PORTA.PIN1CTRL = PORT_INVEN_bm | PORT_ISC_LEVEL_gc;
            /* Enable INT0 on this pin */
            PORTA.INT0MASK = PIN1_bm;
            
            /*Setup the Tx and Rx semaphores */
            uartTxSync[0] = xSemaphoreCreateCounting(64, 1);
            uartRxSync[0] = xSemaphoreCreateCounting(64, 0);
            
            break;
            
        case (uint16_t)&EXTUART1:
            /* PORT A, PIN 0 */
            /* Clear the bit in the DIR register */
            PORTA.DIR &= ~PIN0_bm;
            /* Set the pin for inverted input and Level Sensing Interrupt*/
            PORTA.PIN0CTRL = PORT_INVEN_bm | PORT_ISC_LEVEL_gc;
            /* Enable INT0 on this pin */
            PORTA.INT1MASK = PIN0_bm;

            /*Setup the Tx and Rx semaphores */
            uartTxSync[1] = xSemaphoreCreateCounting(64, 1);
            uartRxSync[1] = xSemaphoreCreateCounting(64, 0);
            
            break;
            
        case (uint16_t)&EXTUART2:
            /* PORT Q, PIN 3 */
            /* Clear the bit in the DIR register */
            PORTQ.DIR &= ~PIN3_bm;
            /* Set the pin for inverted input and Level Sensing Interrupt*/
            PORTQ.PIN3CTRL = PORT_INVEN_bm | PORT_ISC_LEVEL_gc;
            /* Enable INT0 on this pin */
            PORTQ.INT0MASK = PIN3_bm;
            
            /*Setup the Tx and Rx semaphores */
            uartTxSync[2] = xSemaphoreCreateCounting(64, 1);
            uartRxSync[2] = xSemaphoreCreateCounting(64, 0);
            
            break;
            
        case (uint16_t)&EXTUART3:
            /* PORT Q, PIN 2 */
            /* Clear the bit in the DIR register */
            PORTQ.DIR &= ~PIN2_bm;
            /* Set the pin for inverted input and Level Sensing Interrupt*/
            PORTQ.PIN2CTRL = PORT_INVEN_bm | PORT_ISC_LEVEL_gc;
            /* Enable INT0 on this pin */
            PORTQ.INT1MASK = PIN2_bm;
            
            /*Setup the Tx and Rx semaphores */
            uartTxSync[3] = xSemaphoreCreateCounting(64, 1);
            uartRxSync[3] = xSemaphoreCreateCounting(64, 0);
            
            break;
            
        case (uint16_t)&EXTUART4:
            /* PORT B, PIN 0 */
            /* Clear the bit in the DIR register */
            PORTB.DIR &= ~PIN0_bm;
            /* Set the pin for inverted input and Level Sensing Interrupt*/
            PORTB.PIN0CTRL = PORT_INVEN_bm | PORT_ISC_LEVEL_gc;
            /* Enable INT0 on this pin */
            PORTB.INT0MASK = PIN0_bm;
            
            /*Setup the Tx and Rx semaphores */
            uartTxSync[4] = xSemaphoreCreateCounting(64, 1);
            uartRxSync[4] = xSemaphoreCreateCounting(64, 0);
            
            break;
            
        case (uint16_t)&EXTUART5:
            /* PORT B, PIN 1 */
            /* Clear the bit in the DIR register */
            PORTB.DIR &= ~PIN1_bm;
            /* Set the pin for inverted input and Level Sensing Interrupt*/
            PORTB.PIN1CTRL = PORT_INVEN_bm | PORT_ISC_LEVEL_gc;
            /* Enable INT0 on this pin */
            PORTB.INT1MASK = PIN1_bm;
            
            /*Setup the Tx and Rx semaphores */
            uartTxSync[5] = xSemaphoreCreateCounting(64, 1);
            uartRxSync[5] = xSemaphoreCreateCounting(64, 0);

            break;
            
        case (uint16_t)&EXTUART6:
            /* PORT C, PIN 0 */
            /* Clear the bit in the DIR register */
            PORTC.DIR &= ~PIN0_bm;
            /* Set the pin for inverted input and Level Sensing Interrupt*/
            PORTC.PIN0CTRL = PORT_INVEN_bm | PORT_ISC_LEVEL_gc;
            /* Enable INT0 on this pin */
            PORTC.INT0MASK = PIN0_bm;
            
            /*Setup the Tx and Rx semaphores */
            uartTxSync[6] = xSemaphoreCreateCounting(64, 1);
            uartRxSync[6] = xSemaphoreCreateCounting(64, 0);

            break;
            
        case (uint16_t)&EXTUART7:
            /* PORT C, PIN 1 */
            /* Clear the bit in the DIR register */
            PORTC.DIR &= ~PIN1_bm;
            /* Set the pin for inverted input and Level Sensing Interrupt*/
            PORTC.PIN1CTRL = PORT_INVEN_bm | PORT_ISC_LEVEL_gc;
            /* Enable INT0 on this pin */
            PORTC.INT1MASK = PIN1_bm;
            
            /*Setup the Tx and Rx semaphores */
            uartTxSync[7] = xSemaphoreCreateCounting(64, 1);
            uartRxSync[7] = xSemaphoreCreateCounting(64, 0);

            break;
            
        case (uint16_t)&USARTC0:
            /* Set the RXD to input */
            PORTC.DIRCLR = PIN2_bm;
            /* Set the TXD pin to output and set it to logic 1 */
            PORTC.DIRSET = PIN3_bm;
            PORTC.OUT |= PIN3_bm;
            
            /* Enable the Transmitter and Receiver on the USART */
            USARTC0.CTRLB = USART_TXEN_bm | USART_RXEN_bm;
            
            /*Setup the Tx and Rx semaphores */
            uartTxSync[8] = xSemaphoreCreateCounting(64, 1);
            uartRxSync[8] = xSemaphoreCreateCounting(64, 0);

            break;
            
        case (uint16_t)&USARTC1:
            /* Set the RXD to input */
            PORTC.DIRCLR = PIN6_bm;
            /* Set the TXD to output and set it to logic 1 */
            PORTC.DIRSET = PIN7_bm;
            PORTC.OUT |= PIN7_bm;
            
            /* Enable the transmitter and Receiver on the USART */
            USARTC1.CTRLB = USART_TXEN_bm | USART_RXEN_bm;
            
            /*Setup the Tx and Rx semaphores */
            uartTxSync[9] = xSemaphoreCreateCounting(64, 1);
            uartRxSync[9] = xSemaphoreCreateCounting(64, 0);

            break;

        case (uint16_t)&USARTD0:
            /* Set the RXD to input */
            PORTD.DIRCLR = PIN2_bm;
            /* Set the TXD to output and set it to logic 1 */
            PORTD.DIRSET = PIN3_bm;
            PORTD.OUT |= PIN3_bm;
            
            /* Enable the transmitter and Receiver on the USART */
            USARTD0.CTRLB = USART_TXEN_bm | USART_RXEN_bm;
            
            /*Setup the Tx and Rx semaphores */
            uartTxSync[10] = xSemaphoreCreateCounting(64, 1);
            uartRxSync[10] = xSemaphoreCreateCounting(64, 0);

            break;
            
        case (uint16_t)&USARTE0:
            /* Set the RXD to input */
            PORTE.DIRCLR = PIN2_bm;
            /* Set the TXD to output and set it to logic 1 */
            PORTE.DIRSET = PIN3_bm;
            PORTE.OUT |= PIN3_bm;
            
            /* Enable the transmitter and Receiver on the USART */
            USARTE0.CTRLB = USART_TXEN_bm | USART_RXEN_bm;
            
            /*Setup the Tx and Rx semaphores */
            uartTxSync[11] = xSemaphoreCreateCounting(64, 1);
            uartRxSync[11] = xSemaphoreCreateCounting(64, 0);

            break;
          
        default:
            break;
    };
    /* access the Enhanced Feature Registers */
    extUart->UART_LCR = 0xbf;
    /* Enable the Enhanced Features */
    extUart->UART_IIR_FCR |= (1<<4);
};

/* Function to send a character over a serial port */
int8_t sendChar (void *Uart, char toSend, portTickType timeout)
{
    // utility index number
    uint8_t index;
    // temp variable
    uint8_t temp;
    
    if ((uint16_t)Uart >= UART_BASE_ADDR)
    {
        // Shorthand to cast the void to a external uart type
        UART_REG_t *extUart = (UART_REG_t*)Uart;
        
        /* For the external UARTs we can take bits 6:3 of the address as the index into the array */
        index = ((uint16_t)Uart & 0x00FF)>>3;
        
        /* Enable the empty interrupt by writing 1 to IER[1] */
        extUart->UART_IER |= (1<<1);
        
        /* Try to grab the semaphore so that indicates the uart is available for sending */
        if (xSemaphoreTake(uartTxSync[index], timeout) == pdTRUE)
        {
            /* Write the character to the THR */
            extUart->UART_RHR_THR = toSend;
            
            /* wait for the character to send */
            xSemaphoreTake(uartTxSync[index], timeout);
            
            /* disable the the transmit interrupt by clearing IER[1] */
            extUart->UART_IER &= ~(1<<1);
            return 1;
        }
        else
        {
            return 0;
        }
    }
    else
    {
        // Shorthand to cast the void to an internal uart type
        USART_t *intUart = (USART_t*)Uart;
        
        /* We're dealing with the internal UARTs */
        /* Figure out the index of the uart */
        temp = (uint8_t)((uint16_t)Uart >> 4);
        switch (temp) {
            case 0x8A: // USARTC0
                index = 8;
                break;
            case 0x8B: // USARTC1
                index = 9;
                break;
            case 0x9A: // USARTD0
                index = 10;
                break;
            case 0xAA: // USARTE0
                index = 11;
                break;
            default:
                return -1;
                break;
        }

        // Enable the empty interrupt
        intUart->CTRLA |= USART_TXCINTLVL_MED_gc;

        /* wait for the port to become available */
        if (xSemaphoreTake(uartTxSync[index], timeout) == pdTRUE)
        {
            /* Write the character to the buffer */
            intUart->DATA = toSend;
            /* Wait for the character to send */
            xSemaphoreTake(uartTxSync, timeout);
            
            /* Disable the interrupt */
            intUart->CTRLA &= ~USART_TXCINTLVL_gm;
            return 1;
        }
        else
        {
            return 0;
        }
    }
}

/* Function to read a character from a serial port */
int8_t readChar(void *Uart, char *toRead, portTickType timeout)
{
    /* uart index register */
    uint8_t index;
    // temp variable
    uint8_t temp;
    
    /* Check to see if we're reading from external uart */
    if ((uint16_t)Uart >= UART_BASE_ADDR)
    {
        // cast the void pointer to an external uart type
        UART_REG_t *extUart = (UART_REG_t*)Uart;
        
        /* For the external UARTs we can take bits 6:3 of the address as the index into the array */
        index = ((uint16_t)Uart & 0x00FF)>>3;
        
        // enable the Receive holding interrupt by sitting IER[0] to 1
        extUart->UART_IER |= 0x01;
        
        /* wait for a character to arrive */
        if (xSemaphoreTake(uartRxSync[index], timeout) == pdTRUE)
        {
            // copy the data out of the UART
            *toRead = extUart->UART_RHR_THR;
            // disable the interrupt
            extUart->UART_IER &= ~0x01;
            return 1;
        }
        else
        {
            return 0;
        }
    }
    else
    {
        // Shorthand to cast the void to an internal uart type
        USART_t *intUart = (USART_t*)Uart;
        
        /* We're dealing with the internal UARTs */
        /* Figure out the index of the uart */
        temp = (uint8_t)((uint16_t)Uart >> 4);
        switch (temp) {
            case 0x8A: // USARTC0
                index = 8;
                break;
            case 0x8B: // USARTC1
                index = 9;
                break;
            case 0x9A: // USARTD0
                index = 10;
                break;
            case 0xAA: // USARTE0
                index = 11;
                break;
            default:
                return -1;
                break;
        }
        
        // Enable the rx interrupt
        intUart->CTRLA |= USART_RXCINTLVL_MED_gc;
        
        /* wait for a character to arrive */
        if (xSemaphoreTake(uartRxSync[index], timeout) == pdTRUE)
        {
            *toRead = intUart->DATA;
            return 1;
        }
        else
        {
            return 0;
        }
    }
    
}

/* Function to send a string out a uart */
int8_t sendLine (void *Uart, char *toSend, uint8_t txLength)
{
    // utility index number
    uint8_t index;
    // temp variable
    uint8_t temp;
    // position variable
    uint8_t pos;
    
    // Check to see if this is one of the external UARTs
    if ((uint16_t)Uart >= UART_BASE_ADDR)
    {
        // Shorthand to cast the void to a external uart type
        UART_REG_t *extUart = (UART_REG_t*)Uart;
        
        // Variable used to send the DMA request
        DMA_REQ_t dmaReq;
        
        /* For the external UARTs we can take bits 6:3 of the address as the index into the array */
        index = ((uint16_t)Uart & 0x00FF)>>3;
        
        // Set the DMA transfer size
        dmaReq.count = BLOCK_LENGTH;
        
        // Setup the first part of the DMA structure
        dmaReq.reqTask = xTaskGetCurrentTaskHandle();
        
        // Set the destination address (ie the THR)
        dmaReq.dmaDest = (void*)&extUart->UART_RHR_THR;
        
        // Setup the configuration for the DMA transfer
        // Destination address is fixed, source address increments
        dmaReq.CTRL = DMA_CH_DESTDIR_FIXED_gc | DMA_CH_SRCDIR_INC_gc;
    
        
        // Setup the transmit TLR register
        // Set MCR[6] to 1
        extUart->UART_MCR |= (1<<6);
        // Set the lower nibble of the TLR (SPR)
        temp = extUart->UART_SPR;
        temp &= 0xF0;
        extUart->UART_SPR = (temp|(BLOCK_LENGTH>>2));
        // clear MCR[6]
        extUart->UART_MCR &= ~(1<<6);
        
        /* Enable the empty interrupt by writing 1 to IER[1] */
        extUart->UART_IER |= (1<<1);
        
        /* Start at the biggining of the string */
        pos = 0;
        /* Wait for the port to come available */
        if (xSemaphoreTake(uartTxSync[index], portMAX_DELAY) == pdTRUE)
        {
            /* Check to see if there is a full block left */
            while ((pos + BLOCK_LENGTH) < txLength)
            {
                /* There is */
                dmaReq.dmaSrc = (toSend+pos);
                
                /* Add the DMA request */
                xQueueSendToBack(dmaReqQueue, &dmaReq, portMAX_DELAY);
                // GO to sleep until the DMA transfer is complete
                vTaskSuspend(NULL);
                // DMA transfer is done, wait for the UART to become available
                xSemaphoreTake(uartTxSync[index], portMAX_DELAY);
                pos = pos + BLOCK_LENGTH;
            }
            // Set our final block
            dmaReq.dmaSrc = (toSend +pos);
            
            // Set the final count
            dmaReq.count = txLength - pos;
            
            // Setup the DMA transfer
            xQueueSendToBack(dmaReqQueue, &dmaReq, portMAX_DELAY);
            
            // Go to sleep until the DMA transfer is complete
            vTaskSuspend(NULL);
            
            // Wait for the transmission to mostly complete
            xSemaphoreTake(uartTxSync[index], portMAX_DELAY);
            
            // Disable IER
            extUart->UART_IER &= ~(1<<1);
            return 1;
        }
    }
    else
    {
        // We're dealing with the internal uarts
        // Shorthand to cast the void to an internal uart type
        USART_t *intUart = (USART_t*)Uart;
        
        /* We're dealing with the internal UARTs */
        /* Figure out the index of the uart */
        temp = (uint8_t)((uint16_t)Uart >> 4);
        switch (temp) {
            case 0x8A: // USARTC0
                index = 0;
                break;
            case 0x8B: // USARTC1
                index = 1;
                break;
            case 0x9A: // USARTD0
                index = 2;
                break;
            case 0xAA: // USARTE0
                index = 3;
                break;
            default:
                return -1;
                break;
        };
        
        intUartSendString[index] = toSend;
        intUartSendPos[index] = 0;
        intUartSendLength[index] = txLength;
        
        // Enable the empty interrupt
        intUart->CTRLA |= USART_TXCINTLVL_MED_gc;
        
        // wait for the string to finish sending
        xSemaphoreTake(uartTxSync[index + 8], portMAX_DELAY);
        
        /* Iterrate over the string and send it out */
        for (pos = 0; pos < txLength; pos++)
        {
            // Wait for the port to become available
            xSemaphoreTake(uartTxSync[index], portMAX_DELAY);
            // port is available, copy the byte
        intUart->DATA = *(toSend + pos);
        }
        // Disable the interrupt
        intUart->CTRLA &= ~USART_TXCINTLVL_gm;
        return 1;
    }
    // will never get here
    return 0;
}

int8_t readLine (void *Uart, char *toRead, uint8_t rxLength, char termChar)
{
    /* uart index register */
    uint8_t index;
    /* temp variable */
    uint8_t temp;
    /* finished flag */
    uint8_t finished = 0;
    /* Position variable */
    uint8_t pos = 0;
    
    
    /* Check to see if we're reading from an external uart */
    if ((uint16_t)Uart >= UART_BASE_ADDR)
    {
        // cast the void pointer to an external uart type
        UART_REG_t *extUart = (UART_REG_t*)Uart;
        
        /* For the external UARTs we can take bits 6:3 of the address as the index into the array */
        index = ((uint16_t)Uart & 0x00FF)>>3;
        
        // Set the termination char in the UART
        // Save LCR
        temp = extUart->UART_LCR;
        // Set LCR to 0xBF
        extUart->UART_LCR = 0xBF;
        // write the character to Xoff2
        extUart->UART_SPR = termChar;
        // enable special character detect by setting EFR[5]
        extUart->UART_IIR_FCR |= (1<<5);
        // restore LCR
        extUart->UART_LCR = temp;
        
        // Setup the TLR register to capture BLOCK_LENGTH characters
        // Set MCR[6] to 1
        extUart->UART_MCR |= (1<<6);
        // the RX Levels are in TLR[7:4]
        // Snag the TLR
        temp = extUart->UART_SPR;
        // zero out the top nibble
        temp &= 0x0F;
        // Set the high nibble to BLOCK_LENGTH/4
        temp |= ((BLOCK_LENGTH>>2)<<4);
        // write it back to the TLR
        extUart->UART_SPR = temp;
        // clear MCR[6]
        extUart->UART_MCR &= ~(1<<6);
        
        // Enable the interrupts turnon IER[5] and IER[0]
        extUart->UART_IER |= ((1<<5)|(1<<0));
      
        // Start the recieve looop
        while (!finished)
        {
            // Wait for the interrupt to trigger
            if (xSemaphoreTake(uartRxSync[index], portMAX_DELAY) == pdTRUE)
            {
                // Pull Everything out of the fifo we can
                // itterrate on whether there is data in the FIFO
                while (extUart->UART_LSR & 0x01) {
                    // Read the byte in
                    *(toRead+pos) = extUart->UART_RHR_THR;
                    // Check to see if we received the termination char
                    if (*(toRead+pos) == termChar)
                    {
                        finished = 1;
                        break;
                    };
                    // increment the position count
                    pos++;
                    // Check to see if we hit our length
                    if (pos == rxLength) {
                        finished = 1;
                        break;
                        
                    }
                };
                // Go around and wait for the next interrupt
            };
        };
        // Disable the interrupts by clearing IER[5] and IER[0]
        extUart->UART_IER &= ~((1<<5)|(1<<0));
        // return the position
        return pos;
    }
    else
    {
        // we're reading from the internal UARTs.
        // We're dealing with the internal uarts
        // Shorthand to cast the void to an internal uart type
        USART_t *intUart = (USART_t*)Uart;
        
        /* We're dealing with the internal UARTs */
        /* Figure out the index of the uart */
        temp = (uint8_t)((uint16_t)Uart >> 4);
        switch (temp) {
            case 0x8A: // USARTC0
                index = 0;
                break;
            case 0x8B: // USARTC1
                index = 1;
                break;
            case 0x9A: // USARTD0
                index = 2;
                break;
            case 0xAA: // USARTE0
                index = 3;
                break;
            default:
                return -1;
                break;
        };
        
        // Save the Rx Length
        intUartRxLength[index] = rxLength;
        // Clear the position
        intUartRxPos[index] = 0;
        // Save the Rx String Address
        intUartRxString[index] = toRead;
        // Save the term character
        intUartTerm[index] = termChar;
        
        // Enable the receive interrupt
        intUart->CTRLA |= USART_RXCINTLVL_MED_gc;
        
        // Wait for the rx to finish
        xSemaphoreTake(uartRxSync[index+8], portMAX_DELAY);
        
        // disable the interrupt
        intUart->CTRLA &= ~USART_RXCINTLVL_MED_gc;
        // return the number of received characters
        return intUartRxLength[index];
    };
    // we should never ever get here
    return -1;
}

// Interrupt handler for external uart 0
ISR( PORTA_INT0_vect)
{
    uint8_t temp;
    // Read in the IIR
    temp = EXTUART0.UART_IIR_FCR;
    // Check to see if we have a THR interrupt
    if (temp &(1<<1))
    {
        xSemaphoreGiveFromISR(uartTxSync[0], NULL);
    }
    // check to see if we have a receive interrupt.  Basically check that it isn't just IIR[1] set
    if ((temp &(~(1<<1))))
    {
        // It's a receive interrupt
        xSemaphoreGiveFromISR(uartRxSync[0], NULL);
        uartIntStatus[0] = temp;
    }

}

// Interrupt handler for external uart 1
ISR( PORTA_INT1_vect)
{
    uint8_t temp;
    // Read in the IIR
    temp = EXTUART1.UART_IIR_FCR;
    // Check to see if we have a THR interrupt
    if (temp &(1<<1))
    {
        xSemaphoreGiveFromISR(uartTxSync[1], NULL);
    }
    // check to see if we have a receive interrupt.  Basically check that it isn't just IIR[1] set
    if ((temp &(~(1<<1))))
    {
        // It's a receive interrupt
        xSemaphoreGiveFromISR(uartRxSync[1], NULL);
        uartIntStatus[1] = temp;
    }
   }

// Interrupt handler for external uart 2
ISR( PORTQ_INT0_vect)
{
    uint8_t temp;
    // Read in the IIR
    temp = EXTUART2.UART_IIR_FCR;
    // Check to see if we have a THR interrupt
    if (temp &(1<<1))
    {
        xSemaphoreGiveFromISR(uartTxSync[2], NULL);
    }
    // check to see if we have a receive interrupt.  Basically check that it isn't just IIR[1] set
    if ((temp &(~(1<<1))))
    {
        // It's a receive interrupt
        xSemaphoreGiveFromISR(uartRxSync[2], NULL);
        uartIntStatus[2] = temp;
    }
}

// Interrupt handler for external uart 3
ISR( PORTQ_INT1_vect)
{
    uint8_t temp;
    // Read in the IIR
    temp = EXTUART3.UART_IIR_FCR;
    // Check to see if we have a THR interrupt
    if (temp &(1<<1))
    {
        xSemaphoreGiveFromISR(uartTxSync[3], NULL);
    }
    // check to see if we have a receive interrupt.  Basically check that it isn't just IIR[1] set
    if ((temp &(~(1<<1))))
    {
        // It's a receive interrupt
        xSemaphoreGiveFromISR(uartRxSync[3], NULL);
        uartIntStatus[3] = temp;
    }
}

// Interrupt handler for external uart 4
ISR( PORTB_INT0_vect)
{
    uint8_t temp;
    // Read in the IIR
    temp = EXTUART4.UART_IIR_FCR;
    // Check to see if we have a THR interrupt
    if (temp &(1<<1))
    {
        xSemaphoreGiveFromISR(uartTxSync[4], NULL);
    }
    // check to see if we have a receive interrupt.  Basically check that it isn't just IIR[1] set
    if ((temp &(~(1<<1))))
    {
        // It's a receive interrupt
        xSemaphoreGiveFromISR(uartRxSync[4], NULL);
        uartIntStatus[4] = temp;
    }
}

// Interrupt handler for external uart 5
ISR( PORTB_INT1_vect)
{
    uint8_t temp;
    // Read in the IIR
    temp = EXTUART5.UART_IIR_FCR;
    // Check to see if we have a THR interrupt
    if (temp &(1<<1))
    {
        xSemaphoreGiveFromISR(uartTxSync[5], NULL);
    }
    // check to see if we have a receive interrupt.  Basically check that it isn't just IIR[1] set
    if ((temp &(~(1<<1))))
    {
        // It's a receive interrupt
        xSemaphoreGiveFromISR(uartRxSync[5], NULL);
        uartIntStatus[5] = temp;
    }
}

// Interrupt handler for external uart 6
ISR( PORTC_INT0_vect)
{
    uint8_t temp;
    // Read in the IIR
    temp = EXTUART6.UART_IIR_FCR;
    // Check to see if we have a THR interrupt
    if (temp &(1<<1))
    {
        xSemaphoreGiveFromISR(uartTxSync[6], NULL);
    }
    // check to see if we have a receive interrupt.  Basically check that it isn't just IIR[1] set
    if ((temp &(~(1<<1))))
    {
        // It's a receive interrupt
        xSemaphoreGiveFromISR(uartRxSync[6], NULL);
        uartIntStatus[6] = temp;
    }
}

// Interrupt handler for external uart 6
ISR( PORTC_INT1_vect)
{
    uint8_t temp;
    // Read in the IIR
    temp = EXTUART7.UART_IIR_FCR;
    // Check to see if we have a THR interrupt
    if (temp &(1<<1))
    {
        xSemaphoreGiveFromISR(uartTxSync[7], NULL);
    }
    // check to see if we have a receive interrupt.  Basically check that it isn't just IIR[1] set
    if ((temp &(~(1<<1))))
    {
        // It's a receive interrupt
        xSemaphoreGiveFromISR(uartRxSync[7], NULL);
        uartIntStatus[7] = temp;
    }
}

// USARTC0 Rx Interrupt
ISR( USARTC0_RXC_vect)
{
    bool finished = finished;
    // Check to see that we're not overrunning our buffer
    if (intUartRxPos[0] < intUartRxLength[0])
    {
        // Copy the character in from the uart and into the buffer
        intUartRxString[0][intUartRxPos[0]] = USARTC0.DATA;
        // Check to see if we've received the termination character
        if (intUartRxString[0][intUartRxPos[0]] == intUartTerm[0]) {
            finished = true;
        }
        // Increment the position counter
        intUartRxPos[0]++;
    }
    else
    {
        finished = true;
    }
    if (finished)
    {
        // Set the sync and be done
        xSemaphoreGiveFromISR(uartRxSync[8], NULL);
    }
 }

// USARTC0 Tx Interrupt
ISR(USARTC0_TXC_vect)
{
    // Check to see if we've sent all the characters
    if (intUartSendPos[0] < intUartSendLength[0])
    {
        // Copy the next character into the Data register
        USARTC0.DATA = intUartSendString[0][intUartSendPos[0]];
        // incriment the position
        intUartSendPos[0]++;
    }
    else
    {
        // We've sent all the data, release the mutex
        xSemaphoreGiveFromISR(uartTxSync[8], NULL);
    }
}

// USARTC1 Rx Interrupt
ISR( USARTC1_RXC_vect)
{
    bool finished = finished;
    // Check to see that we're not overrunning our buffer
    if (intUartRxPos[1] < intUartRxLength[1])
    {
        // Copy the character in from the uart and into the buffer
        intUartRxString[1][intUartRxPos[1]] = USARTC1.DATA;
        // Check to see if we've received the termination character
        if (intUartRxString[1][intUartRxPos[1]] == intUartTerm[1]) {
            finished = true;
        }
        // Increment the position counter
        intUartRxPos[1]++;
    }
    else
    {
        finished = true;
    }
    if (finished)
    {
        // Set the sync and be done
        xSemaphoreGiveFromISR(uartRxSync[9], NULL);
    }
}

// USARTC1 Tx Interrupt
ISR(USARTC1_TXC_vect)
{
    // Check to see if we've sent all the characters
    if (intUartSendPos[1] < intUartSendLength[1])
    {
        // Copy the next character into the Data register
        USARTC1.DATA = intUartSendString[1][intUartSendPos[1]];
        // incriment the position
        intUartSendPos[1]++;
    }
    else
    {
        // We've sent all the data, release the mutex
        xSemaphoreGiveFromISR(uartTxSync[9], NULL);
    }
}

// USARTD0 Rx Interrupt
ISR( USARTD0_RXC_vect)
{
    bool finished = finished;
    // Check to see that we're not overrunning our buffer
    if (intUartRxPos[2] < intUartRxLength[2])
    {
        // Copy the character in from the uart and into the buffer
        intUartRxString[2][intUartRxPos[2]] = USARTD0.DATA;
        // Check to see if we've received the termination character
        if (intUartRxString[2][intUartRxPos[2]] == intUartTerm[2]) {
            finished = true;
        }
        // Increment the position counter
        intUartRxPos[2]++;
    }
    else
    {
        finished = true;
    }
    if (finished)
    {
        // Set the sync and be done
        xSemaphoreGiveFromISR(uartRxSync[10], NULL);
    }
}

// USARTD0 Tx Interrupt
ISR(USARTD0_TXC_vect)
{
    // Check to see if we've sent all the characters
    if (intUartSendPos[2] < intUartSendLength[2])
    {
        // Copy the next character into the Data register
        USARTD0.DATA = intUartSendString[2][intUartSendPos[2]];
        // incriment the position
        intUartSendPos[2]++;
    }
    else
    {
        // We've sent all the data, release the mutex
        xSemaphoreGiveFromISR(uartTxSync[10], NULL);
    }
}

// USARTE0 Rx Interrupt
ISR( USARTE0_RXC_vect)
{
    bool finished = finished;
    // Check to see that we're not overrunning our buffer
    if (intUartRxPos[3] < intUartRxLength[3])
    {
        // Copy the character in from the uart and into the buffer
        intUartRxString[3][intUartRxPos[3]] = USARTC0.DATA;
        // Check to see if we've received the termination character
        if (intUartRxString[3][intUartRxPos[3]] == intUartTerm[3]) {
            finished = true;
        }
        // Increment the position counter
        intUartRxPos[3]++;
    }
    else
    {
        finished = true;
    }
    if (finished)
    {
        // Set the sync and be done
        xSemaphoreGiveFromISR(uartRxSync[11], NULL);
    }
}

// USARTE0 Tx Interrupt
ISR(USARTE0_TXC_vect)
{
    // Check to see if we've sent all the characters
    if (intUartSendPos[3] < intUartSendLength[3])
    {
        // Copy the next character into the Data register
        USARTE0.DATA = intUartSendString[3][intUartSendPos[3]];
        // incriment the position
        intUartSendPos[3]++;
    }
    else
    {
        // We've sent all the data, release the mutex
        xSemaphoreGiveFromISR(uartTxSync[11], NULL);
    }
}
